/*
    Android UIML

	Copyright (C) 2010  Bram Goffings (bramgoffings@gmail.com)

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public License
	as published by the Free Software Foundation; either version 2.1
	of	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

package uiml.android.rendering;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;

import uiml.android.RenderTools.RenderUtils;
import uiml.android.elements.Behavior;
import uiml.android.elements.Logic;
import uiml.android.elements.Part;
import uiml.android.elements.Property;
import uiml.android.elements.Style;
import uiml.android.elements.UimlDocument;
import uiml.android.elements.peers.Peer;
import uiml.android.elements.peers.Presentation;
import uiml.android.elements.peers.Vocabulary;
import uiml.android.executing.Rule;
import uiml.android.main.AndroidUiml;
import android.view.View;
import android.view.ViewGroup;

public class Renderer {

	// TODO comment
	UimlDocument doc = null;
	Vocabulary voc = null;
	Part top = null;
	Style style = null;
	Behavior behavior = null;
	Logic logic = null;
	
	// TODO comment
	TypeDecoder typeDecoder;
	
	
	public Renderer(UimlDocument doc, TypeDecoder dec) {
		this.doc = doc;
		typeDecoder = dec;
		initiliaze();
	}
	
	public void initiliaze() {
		searchVoc();
		searchTop();
		searchStyle();
		searchBehavior();
		searchLogic();
		
		if(logic!=null)
			voc.addLogic(logic);
	}
	
	public void searchVoc() {
		// TODO At the moment we support 1 peer with 1 presentation
		Peer currentPeer =  doc.getRoot().getPeers().get(0);
		Presentation presentation = currentPeer.getPresentation().get(0);
		voc = presentation.getVoc();	
	}
	
	public void searchLogic() {
		// TODO At the moment we support 1 peer with one block of logic
		Peer currentPeer =  doc.getRoot().getPeers().get(0);
		
		if(currentPeer.getLogic().size()>0)
			logic = currentPeer.getLogic().get(0);	
	}
	
	
	
	public void searchTop() {
		top =  doc.getRoot().getInterf().getStructures().get(0).getTop();
	}
	
	public void searchStyle() {
		style =  doc.getRoot().getInterf().getStyles().get(0);
	}
	
	public void searchBehavior() {
		try {
			behavior =  doc.getRoot().getInterf().getBehaviors().get(0);
		}
		catch(Exception e) {
			behavior = null;
		}
	}
	
	/**
	 * Start render functie.
	 * 
	 * @return
	 */
	public View render() {
		View root = render(top, style);
		renderEvents(root, top, behavior);
		return root;
	}
	

	private void renderEvents(View rootView, Part part, Behavior behavior) {
		
		View found = rootView.findViewWithTag((Object)part.getIdentifier());
		applyEvents((Object)found, part, behavior);
		
		if(part.hasSubParts())
		{
			for(Part subPart : part.getSubParts())
				renderEvents(rootView, subPart, behavior);
		}
		
	}

	/**
	 * Main render function.
	 *
	 * Adds recursivly all the parts (with style) of the tree.
	 * 
	 * @param uiPart
	 * @param uiStyle
	 * 
	 * @return View
	 */
	public View render(Part uiPart, Style uiStyle)
	{
		String className = voc.mapsOnCls(uiPart.getClas());
		Object obj = null;
		
		try {
			
			Class<?> c = Class.forName(className);
			
			// Ther eis no "zero" constructor
			// We fetch every available constructor.
			// We select the first.
			// It needs the context as param
			// The Context in this application is reachable through AndroidUiml.me
			Constructor<?>[] construct = c.getConstructors();
			obj = construct[0].newInstance(AndroidUiml.me);
			
			// Give every view a (unique) tag
			((View)obj).setTag(uiPart.getIdentifier());
			
			// If the part contains additional subpars then the current part can be seen as a "ViewGroup"
			// ViewGroups aren't visible. The only goal of them is to group items.
			// Every View in android has to be in a ViewGroup.
			if(uiPart.hasSubParts())
			{
				for(Part part : uiPart.getSubParts()) {
					View newView = render(part, uiStyle);					
					((ViewGroup)obj).addView(newView);			
				}
			}
			
			// Apply properties en events
			applyProperties(obj, uiPart, uiStyle);
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
		return (View)obj;
	}

	/**
	 * 
	 * @param obj
	 * @param uiPart
	 * @param uiStyle
	 * @return
	 */
	private void applyProperties(Object obj, Part uiPart, Style uiStyle) {
		
		ArrayList<Property> matchedProperties = uiStyle.getProperties(uiPart.getIdentifier());
		
		for(Property property : matchedProperties) {
			
			// Get custom decoder
			// Some layout elements can not be added through reflection
			// We have to filter those and add them hardcoded
			CustomTypeDecoder customDecoder = typeDecoder.getDecoder();
			
			if(!customDecoder.contains(property.getName(), uiPart.getClas(), "setMethod"))
				RenderUtils.applyProperty(obj, uiPart, property, "setMethod", voc);
			else 
				typeDecoder.applyNonReflectionProperties((View)obj, property.getName(), "setMethod", property.getValue());
		}
	}
	
	
	
	/**
	 * 
	 * @param obj
	 * @param behavior
	 */
	private void applyEvents(Object obj, Part uiPart, Behavior uiBehavior) {
		
		if(uiBehavior != null) {
		
			ArrayList<Rule> rules  = uiBehavior.getRules();
			
			for(int i=0; i<rules.size(); ++i)
				if(rules.get(i).getPartNameInvoker().equals(uiPart.getIdentifier()))
					rules.get(i).execute(obj, uiPart, voc, logic);
		}
	}
	
	
}
